﻿Function Get-RDSActiveManagementServer($brokerComputerName,$credential)
{
    Import-Module -Force ..\BuildDeployment\libUtilities.psm1

    $brokerSession=Assert-PSSession -computerAddress $brokerComputerName -credential $credential -useCredSSP
    $command={ param($brokerComputerName)
               (Get-RDConnectionBrokerHighAvailability -ConnectionBroker $brokerComputerName).ActiveManagementServer}
    $activeManagementServer=Invoke-Command -Session $brokerSession -ScriptBlock $command -ArgumentList $brokerComputerName

    $activeManagementServer
        
}
Function Get-RDSActiveUserSession($brokerComputerName,$credential)
{
    Import-Module -Force ..\BuildDeployment\libUtilities.psm1

    $activeManagementServer=Get-RDSActiveManagementServer -brokerComputerName $brokerComputerName -credential $credential

    $activeManagementServerSession=Assert-PSSession -computerAddress $activeManagementServer -credential $credential -useCredSSP

    $command={Get-RDUserSession | Where-Object {$_.SessionState -eq "STATE_ACTIVE"}}

    $activeUserSessions=Invoke-Command -Session $activeManagementServerSession -ScriptBlock $command

    $activeUserSessions
}

Function Get-RDSPerfmonStatistics($brokerComputerNames,$credential,$outputType,$logFilePath,$refreshIntervalSeconds=900)
{
    Import-Module -Force .\libWindowsPerformance.psm1
    Import-Module -Force .\PSTerminalServices\PSTerminalServices.psm1

    $sampleIntervalSeconds=5

    $executionTime=(Get-Date).GetDateTimeFormats("u").Replace(":","")
    Add-Type -Path .\PSTerminalServices\Bin\Cassia.dll

    $partNumber=1
    while($true)
    {
        $computerNames=@()
        $sessionInfo=@()
        foreach($brokerComputerName in $brokerComputerNames)
        {
            $activeSessions=Get-RDSActiveUserSession -brokerComputerName $brokerComputerName -credential $credential

            
            $logFileName="$executionTime`_RDS_Session_$brokerComputerName`_Stats_Part_$partNumber.blg"
            $sessionInfoFileName="$executionTime`_RDS_Session_$brokerComputerName`_SessionInfo_Part_$partNumber.csv"
            $activeDirectoryDomains=$activeSessions.DomainName | Sort-Object -Unique | Get-ADDomain
            


            foreach($activeSession in $activeSessions)
            {
                $computerNames+=$activeSession.ServerName
                $adUser=Get-ADUser $activeSession.UserName -Properties Company,Title -Server (($activeDirectoryDomains | Where-Object {$_.NetBIOSName -eq $activeSession.DomainName}).InfrastructureMaster)

                
                $rdsSession=Get-TSSession -ComputerName $activeSession.ServerName -Id $activeSession.SessionId | Select *
                
                if($rdsSession.WindowStationName -eq $null)
                {
                    $psExecCommand=".\Tools\psexec.exe \\" + $activeSession.ServerName + " query session 2>&1"
                    $querySessionOutput=(cmd /s /c $psExecCommand | select-string $activeSession.UserName)

                    if($querySessionOutput -ne $null)
                    {
                        $rdpSessionName=$querySessionOutput.Line.Substring(0,12).TrimStart().TrimEnd().Replace("#"," ")
                    }
                }
                else
                {
                    $rdpSessionName=$rdsSession.WindowStationName.Replace("#"," ")
                }

                if($rdsSession.ClientIPAddress -eq $null)
                {                
                    $clientComputerIPAddress=""    
                }
                else
                {
                    $clientComputerIPAddress=$rdsSession.ClientIPAddress.ToString();    
                }

                if($rdsSession.ClientName-eq $null)
                {                
                    $clientComputerName=""    
                }
                else
                {
                    $clientComputerName=$rdsSession.ClientName;
                }

                

                $sessionInfo+=New-Object -TypeName PSObject -Property @{UserName=$activeSession.UserName;
                                                                        UserDomain=$activeSession.DomainName;
                                                                      ComputerName=$activeSession.ServerName;
                                                                      ResolutionWidth=$activeSession.ResolutionWidth;
                                                                      ResolutionHeight=$activeSession.ResolutionHeight;
                                                                      ColorDepth=$activeSession.ColorDepth
                                                                      RDSBrokerComputerName=$brokerComputerName;
                                                                      CollectionName=$activeSession.CollectionName;
                                                                      HostServerName=$activeSession.HostServer;
                                                                      UserTitle=if($adUser.Title -ne $null){$adUser.Title} else {""};
                                                                      UserCompany=if($adUser.Company -ne $null){$adUser.Company} else {""};
                                                                      UserClientComputerIP=$clientComputerIPAddress;
                                                                      UserClientComputerName=$clientComputerName;
                                                                      RDPUnifiedSessionId=$activeSession.UnifiedSessionId;
                                                                      RDPSessionId=$activeSession.SessionId;
                                                                      RDPSessionName=$rdpSessionName;} 

                if($outputType -eq "Log")
                {
                    $sessionInfo | Export-Csv -NoTypeInformation -Path $logFilePath$sessionInfoFileName -Append
                }
            }
        }



        if($computerNames.Count -gt 0)
        {
            Write-Host "Starting perfmon collection for the computers"
            if($outputType -eq "Log")
            {
                Get-PerformanceMonitoring -computers $computerNames -templates Detailed_RDS -outputType log -tag RDSUsers_$brokerComputerName -logFilePath $logFilePath -sampleInterval $sampleIntervalSeconds -maxSamples ($refreshIntervalSeconds/$sampleIntervalSeconds) -logFileName $logFileName
                $partNumber++
            }
            elseif($outputType -eq "Stream")
            {
                Get-PerformanceMonitoring -computers $computerNames -templates Detailed_RDS -outputType stream -sampleInterval $sampleIntervalSeconds -maxSamples ($refreshIntervalSeconds/$sampleIntervalSeconds) | Normalize-RDSPerfmonSample -sessionInfo $sessionInfo | Store-RDSNormalizedPerfmonSample
            }

            
            Write-Host "Refreshing sessions and computers list"
        }
        else
        {
            Write-Host "No active session to monitor"
        }
    }
}

Function Normalize-RDSPerfmonSample($sessionInfo)
{
    begin
    {
        Import-Module -Force .\libWindowsPerformance.psm1
    }
    process
    {
        $intervalCounterSamples=$_.CounterSamples
        $sampleTime=$_.Timestamp


        $parsedSamples=@()
        foreach($intervalCounterSample in $intervalCounterSamples)
        {
            $parsedSamples+=$intervalCounterSample | Parse-CounterSample -sampleTime  $sampleTime
        }


        foreach($session in $sessionInfo)
        {
            $sessionSamples=$parsedSamples | Where-Object {$_.ComputerName -eq $session.ComputerName -and $_.CounterInstanceName -ilike "*"+ $session.RDPSessionName +"*"}

            if($sessionSamples -eq $null)
            {
                $sessionSamples=$parsedSamples | Where-Object {$_.ComputerName -eq $session.ComputerName}   
            }
            

            $counterInstanceCount=(@($sessionSamples | Where-Object {$_.CounterCategory -ieq "remotefx network" -and $_.CounterName -ieq "current tcp rtt"})).Count
            $workingSetInstances=(($sessionSamples | Where-Object {$_.CounterName -eq "working set" }).CounterValue/1MB)


            if($counterInstanceCount -eq 1)
            {
                $normalizedSample=New-Object -TypeName PSObject
                $normalizedSample | Add-Member -MemberType NoteProperty -Name ComputerName -Value $session.ComputerName
                $normalizedSample | Add-Member -MemberType NoteProperty -Name UserName -Value $session.UserName
                $normalizedSample | Add-Member -MemberType NoteProperty -Name UserDomain -Value $session.UserDomain
                $normalizedSample | Add-Member -MemberType NoteProperty -Name SampleTime -Value $sampleTime
                $normalizedSample | Add-Member -MemberType NoteProperty -Name TCPLatencyMs -Value ($sessionSamples | Where-Object {$_.CounterName -eq "current tcp rtt" }).CounterValue
                $normalizedSample | Add-Member -MemberType NoteProperty -Name UDPLatencyMs -Value ($sessionSamples | Where-Object {$_.CounterName -eq "current udp rtt" }).CounterValue
                $normalizedSample | Add-Member -MemberType NoteProperty -Name DataSentRate -Value ($sessionSamples | Where-Object {$_.CounterName -eq "total sent rate" }).CounterValue
                $normalizedSample | Add-Member -MemberType NoteProperty -Name DataReceivedRate -Value ($sessionSamples | Where-Object {$_.CounterName -eq "total received rate" }).CounterValue

                $normalizedSample | Add-Member -MemberType NoteProperty -Name FramesSkippedByClient -Value ($sessionSamples | Where-Object {$_.CounterName -eq "frames skipped/second - insufficient client resources" }).CounterValue
                $normalizedSample | Add-Member -MemberType NoteProperty -Name FramesSkippedByNetwork -Value ($sessionSamples | Where-Object {$_.CounterName -eq "frames skipped/second - insufficient network resources" }).CounterValue
                $normalizedSample | Add-Member -MemberType NoteProperty -Name FramesSkippedByServer -Value ($sessionSamples | Where-Object {$_.CounterName -eq "frames skipped/second - insufficient server resources" }).CounterValue
                $normalizedSample | Add-Member -MemberType NoteProperty -Name AverageEncodingTime -Value ($sessionSamples | Where-Object {$_.CounterName -eq "average encoding time" }).CounterValue

                $normalizedSample | Add-Member -MemberType NoteProperty -Name WorkingSetMB -Value (($sessionSamples | Where-Object {$_.CounterName -eq "working set" }).CounterValue/1MB)
                $normalizedSample | Add-Member -MemberType NoteProperty -Name PercentProcessorTime -Value ($sessionSamples | Where-Object {$_.CounterName -eq "% processor time" }).CounterValue

                $normalizedSample | Add-Member -MemberType NoteProperty -Name RDSBrokerComputerName -Value $session.RDSBrokerComputerName
                $normalizedSample | Add-Member -MemberType NoteProperty -Name CollectionName -Value $session.CollectionName
                $normalizedSample | Add-Member -MemberType NoteProperty -Name HostServerName -Value $session.HostServerName
                $normalizedSample | Add-Member -MemberType NoteProperty -Name UserCompany -Value $session.UserCompany
                $normalizedSample | Add-Member -MemberType NoteProperty -Name UserTitle -Value $session.UserTitle

                $normalizedSample | Add-Member -MemberType NoteProperty -Name UserClientComputerIP -Value $session.UserClientComputerIP
                $normalizedSample | Add-Member -MemberType NoteProperty -Name UserClientComputerName -Value $session.UserClientComputerName

                $normalizedSample   
                
            }
        }
    }
}

Function Store-RDSNormalizedPerfmonSample()
{
    begin
    {
        Import-Module -DisableNameChecking -Force ..\ConfigurationDatabase\libConfigurationDatabase.psm1
        $sqlConn = new-object ("Data.SqlClient.SqlConnection") (Get-ConfigurationSetting -environmentShortName $environmentShortName -scope "Global"  -name "VDIMonitoringDatabaseConnectionString").Value

        $varcharDataType=[System.Data.SqlDbType]::VarChar
        $smallDateTimeDataType=[System.Data.SqlDbType]::SmallDateTime
        $floatDataType=[System.Data.SqlDbType]::Float
        $smallIntType=[System.Data.SqlDbType]::SmallInt
        $tinyIntType=[System.Data.SqlDbType]::TinyInt
        $intDataType=[System.Data.SqlDbType]::Int

        $insertCounterSampleQuery="INSERT INTO [dbo].[RDPNetworkStatistics]
                                   ([ComputerName]
                                   ,[UserName]
                                   ,[UserDomain]
                                   ,[SampleTime]
                                   ,[TCPLatencyMs]
                                   ,[UDPLatencyMs]
                                   ,[DataSentRate]
                                   ,[DataReceivedRate]
                                   ,[RDSBrokerComputerName]
                                   ,[CollectionName]
                                   ,[HostServerName]
                                   ,[UserCompany]
                                   ,[UserTitle]
                                   ,[FramesSkippedByClient]
                                   ,[FramesSkippedByNetwork]
                                   ,[FramesSkippedByServer]
                                   ,[AverageEncodingTime]
                                   ,[PercentProcessorTime]
                                   ,[UserClientComputerIP]
                                   ,[UserClientComputerName]
                                    ,[WorkingSetMB])
                             VALUES
                                   (@ComputerName
                                   ,@UserName
                                   ,@UserDomain
                                   ,@SampleTime
                                   ,@TCPLatencyMs
                                   ,@UDPLatencyMs
                                   ,@DataSentRate
                                   ,@DataReceivedRate
                                   ,@RDSBrokerComputerName
                                   ,@CollectionName
                                   ,@HostServerName
                                   ,@UserCompany
                                   ,@UserTitle
                                   ,@FramesSkippedByClient
                                   ,@FramesSkippedByNetwork
                                   ,@FramesSkippedByServer
                                   ,@AverageEncodingTime
                                   ,@PercentProcessorTime
                                   ,@UserClientComputerIP
                                   ,@UserClientComputerName
                                   ,@WorkingSetMB)"

        
        $computerNameParam = New-Object System.Data.SqlClient.SqlParameter "@ComputerName", $varcharDataType
        $userNameParam = New-Object System.Data.SqlClient.SqlParameter "@UserName", $varcharDataType
        $userDomainParam = New-Object System.Data.SqlClient.SqlParameter "@UserDomain", $varcharDataType
        $sampleTimeParam = New-Object System.Data.SqlClient.SqlParameter "@SampleTime", $smallDateTimeDataType
        $tcpLatencyMsParam = New-Object System.Data.SqlClient.SqlParameter "@TCPLatencyMs", $smallIntType
        $udpLatencyMsParam = New-Object System.Data.SqlClient.SqlParameter "@UDPLatencyMs", $smallIntType
        $dataSentRateLatencyMsParam = New-Object System.Data.SqlClient.SqlParameter "@DataSentRate", $floatDataType
        $dataReceivedRateParam = New-Object System.Data.SqlClient.SqlParameter "@DataReceivedRate", $floatDataType
        $rdsBrokerComputerNameParam = New-Object System.Data.SqlClient.SqlParameter "@RDSBrokerComputerName", $varcharDataType
        $collectionNameParam = New-Object System.Data.SqlClient.SqlParameter "@CollectionName", $varcharDataType
        $hostServerNameParam = New-Object System.Data.SqlClient.SqlParameter "@HostServerName", $varcharDataType
        $userCompanyParam = New-Object System.Data.SqlClient.SqlParameter "@UserCompany", $varcharDataType
        $userTitleParam = New-Object System.Data.SqlClient.SqlParameter "@UserTitle", $varcharDataType
        $framesSkippedByClientParam = New-Object System.Data.SqlClient.SqlParameter "@FramesSkippedByClient", $tinyIntType
        $framesSkippedByNetworkParam = New-Object System.Data.SqlClient.SqlParameter "@FramesSkippedByNetwork", $tinyIntType
        $framesSkippedByServerParam = New-Object System.Data.SqlClient.SqlParameter "@FramesSkippedByServer", $tinyIntType
        $averageEncodingTimeParam = New-Object System.Data.SqlClient.SqlParameter "@AverageEncodingTime", $smallintType
        $percentProcessorTimeParam = New-Object System.Data.SqlClient.SqlParameter "@PercentProcessorTime", $tinyIntType
        $userClientComputerIpParam = New-Object System.Data.SqlClient.SqlParameter "@UserClientComputerIP", $varcharDataType
        $userClientComputerNameParam = New-Object System.Data.SqlClient.SqlParameter "@UserClientComputerName", $varcharDataType
        $workingSetMBParam = New-Object System.Data.SqlClient.SqlParameter "@WorkingSetMB", $intDataType


        $insertCounterSampleCommand = New-Object Data.SqlClient.SqlCommand $insertCounterSampleQuery,$sqlConn
        $insertCounterSampleCommand.Parameters.Add($computerNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($userNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($userDomainParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($sampleTimeParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($tcpLatencyMsParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($udpLatencyMsParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($dataSentRateLatencyMsParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($dataReceivedRateParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($rdsBrokerComputerNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($collectionNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($hostServerNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($userCompanyParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($userTitleParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($framesSkippedByClientParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($framesSkippedByNetworkParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($framesSkippedByServerParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($averageEncodingTimeParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($percentProcessorTimeParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($userClientComputerIpParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($userClientComputerNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($workingSetMBParam) | Out-Null

        $sqlConn.Open()
    }
    process
    {
        $normalizedSample=$_
        $computerNameParam.Value=$normalizedSample.ComputerName
        $userNameParam.Value=$normalizedSample.UserName
        $userDomainParam.Value=$normalizedSample.UserDomain
        $sampleTimeParam.Value=$normalizedSample.SampleTime
        $tcpLatencyMsParam.Value=$normalizedSample.TCPLatencyMs
        $udpLatencyMsParam.Value=$normalizedSample.UDPLatencyMs
        $dataSentRateLatencyMsParam.Value=$normalizedSample.DataSentRate
        $dataReceivedRateParam.Value=$normalizedSample.DataReceivedRate
        $rdsBrokerComputerNameParam.Value=$normalizedSample.RDSBrokerComputerName
        $collectionNameParam.Value=$normalizedSample.CollectionName
        $hostServerNameParam.Value=$normalizedSample.HostServerName
        $userCompanyParam.Value=$normalizedSample.UserCompany
        $userTitleParam.Value=$normalizedSample.UserTitle
        $framesSkippedByClientParam.Value=$normalizedSample.FramesSkippedByClient
        $framesSkippedByNetworkParam.Value=$normalizedSample.FramesSkippedByNetwork
        $framesSkippedByServerParam.Value=$normalizedSample.FramesSkippedByServer
        $averageEncodingTimeParam.Value=$normalizedSample.AverageEncodingTime
        $percentProcessorTimeParam.Value=$normalizedSample.PercentProcessorTime
        $userClientComputerIpParam.Value=$normalizedSample.UserClientComputerIp
        $userClientComputerNameParam.Value=$normalizedSample.UserClientComputerName
        $workingSetMBParam.Value=$normalizedSample.WorkingSetMB

        
        try
        {
            $insertCounterSampleCommand.ExecuteNonQuery() | OUt-Null
            Write-Output $_
        }
        catch
        {
            $_.Message
        }
    }
    End
    {
        $sqlConn.Dispose()
    }
}

Function Export-RDSPerfmonStatistics($fileNamePattern,$exportFilePath)
{
    Import-Module -Force .\libWindowsPerformance.psm1

    $statsFiles=Get-ChildItem -Path "$fileNamePattern*.blg" | Sort-Object -Property CreationTime

    foreach($statFile in $statsFiles)
    {
        Write-Host "Processing"$statFile.FullName
        Import-Counter -Path $statFile.FullName -ErrorAction SilentlyContinue | Convert-PerfmonBinaryData -logFilePath $statFile.FullName | Get-RDSPerfmonComputerUser -logFilePath $statFile.FullName  | Export-Csv -NoTypeInformation -Append -Path $exportFilePath
    }
}

Function Get-RDSPerfmonComputerUser($logFilePath)
{
    begin
    {
        $currentSample=New-Object -TypeName PSObject 
        $currentSample | Add-Member -MemberType NoteProperty -Name ComputerName -Value $null
        $currentSample | Add-Member -MemberType NoteProperty -Name SampleTime -Value $null
        $currentSample | Add-Member -MemberType NoteProperty -Name CounterCategory -Value $null
        $currentSample | Add-Member -MemberType NoteProperty -Name CounterName -Value $null
        $currentSample | Add-Member -MemberType NoteProperty -Name InstanceName -Value $null
        $currentSample | Add-Member -MemberType NoteProperty -Name CounterValue -Value $null
        $currentSample | Add-Member -MemberType NoteProperty -Name UserName -Value $null
    }
    process
    {
        $partNumber=$logFilePath.SubString($logFilePath.IndexOf("Part_")+5,$logFilePath.IndexOf(".blg")-($logFilePath.IndexOf("Part_")+5))
        $sessionFile=Get-ChildItem -Path "$fileNamePattern*Part_$partNumber.csv"
        $csv=Import-Csv $sessionFile.FullName
        $counterSample=$_
        $sessionInfo=$csv | Where-Object {$_.ComputerName -ieq $counterSample.ComputerName}
        
               
        $currentSample.ComputerName=$_.ComputerName
        $currentSample.SampleTime=$_.SampleTime
        $currentSample.CounterCategory=$_.CounterCategory
        $currentSample.CounterName=$_.CounterName
        $currentSample.InstanceName=$_.InstanceName
        $currentSample.CounterValue=$_.CounterValue
        $currentSample.UserName=$sessionInfo.UserName

        $currentSample

    }
}
